#include <cassert>

#include "classifier.h"
#include "util.h"

extern pn sw;

classifier::classifier(const vector <string> &p, bool t) {
   prod = p;
   for (int i=0; i<prod.size(); ++i) iprod.push_back(mapInsert(prod[i]));
   nr=false;
   nr_designated=false;
   if (prod[0].size() > 1) {
      if (prod[0][0]=='N' && prod[0][1]=='R') nr=true;
   }
   term = t;
}

classifier::classifier(const vector <string> &p, bool s, bool t) {
   prod = p;
   for (int i=0; i<prod.size(); ++i) iprod.push_back(mapInsert(prod[i]));
   nr=false;
   nr_designated=false;
   if (prod[0].size() > 1) {
      if (prod[0][0]=='N' && prod[0][1]=='R') nr=true;
   }
   st = s;
   term = t;
   // todo: automatically detect auxiliary productions
   if (prod[0]=="S*" || prod[0]=="S" || prod[0]=="SR" || prod[0]=="SE") aux=true;
   else aux=false;
}

double classifier::giveP(int ind) const {
   return giveP(ind,sw);
}
 
double classifier::giveP(int ind, pn &tsw) const {
   double d;
   if (degenerate(d)) return d;
   return skc.giveP(tsw,ind);
}

double classifier::giveDec(int ind) const {
   return giveDec(ind,sw);
}
 
double classifier::giveDec(int ind, pn &tsw) const {
   double d;
   if (degenerate(d)) {
      if (d==0) return -1;
      return d;
   }
   return skc.giveDec(tsw,ind);
}
 

void classifier::addNeg2(posNeg &n) {
   indNeg2.push_back(sw.add(n));
}
void classifier::addNeg1(posNeg &n) {
   indNeg1.push_back(sw.add(n));
}
void classifier::addNeg(posNeg &n) {
   indNeg.push_back(sw.add(n));
}
void classifier::addPos(posNeg &p) {
   indPos.push_back(sw.add(p));
}

bool classifier::degenerate(double &d) const {
   if (indNeg.size() == 0 && indPos.size() != 0) {
      if (indPos.size() > 3) d=1; // parameter
      else d=0;
      return true;
   }
   if (indNeg.size() != 0 && indPos.size() == 0) {
      d=0;
      return true;
   }
   if (indNeg.size() == 0 && indPos.size() == 0) {
      d=0;  // debatable, may be =1
      return true;
   }
   return false;
}


void classifier::train() {
   if (indPos.size()==0 || indNeg.size()==0) return;
   if (nr) {
   //if (nr || prod[0]=="Other") {
      //if (nr_designated) skc.train(indPos,indNeg);
   }
   else skc.train(indPos,indNeg);
}

double classifier::accuracy() {
   /*
   if (indPos.size()==0 || indNeg.size()==0) return -1;
   vector <int> trp,trn,tep,ten;
   for (int i=0; i<indPos.size(); ++i) {
      //if (i % 5==0) {
	 //tep.push_back(indPos[i]);
      //}
      //else {
	 trp.push_back(indPos[i]);
	 tep.push_back(indPos[i]);
      //}
   }
   for (int i=0; i<indNeg.size(); ++i) {
      //if (i % 5==0) {
	 //ten.push_back(indNeg[i]);
      //}
      //else {
	 trn.push_back(indNeg[i]);
	 ten.push_back(indNeg[i]);
      //}
   }

   skc.train(trp,trn);

   int c=0;
   for (int i=0; i<tep.size(); ++i) {
      cout<<skc.giveP(tep[i])<<" pos\n";
      //if (skc.giveDec(tep[i]) >= 0) ++c;
      if (skc.giveP(tep[i]) >= 0.5) ++c;
   }
   for (int i=0; i<ten.size(); ++i) {
      cout<<skc.giveP(ten[i])<<" neg\n";
      //if (skc.giveDec(ten[i]) < 0) ++c;
      if (skc.giveP(ten[i]) < 0.5) ++c;
   }
   if (tep.size() + ten.size() > 0) return 100*c/(tep.size()+ten.size());
   else return -1;
   */
}

void classifier::testEg() const {
   /*writeVector(cout,prod);
   for (int i=0; i<indPos.size(); ++i) {
      cout<<"POS: "<<"\n";
      sw.writeStr(cout,indPos[i]);
      cout<<"PROB: "<<giveP(indPos[i],sw)<<"\n\n";
      cout<<"DEC: "<<giveDec(indPos[i],sw)<<"\n\n";
   }
   for (int i=0; i<indNeg.size(); ++i) {
      cout<<"NEG: "<<"\n";
      sw.writeStr(cout,indNeg[i]);
      cout<<"PROB: "<<giveP(indNeg[i],sw)<<"\n\n";
      cout<<"DEC: "<<giveDec(indNeg[i],sw)<<"\n\n";
   }
   cout<<"\n\n";
   */
}

void classifier::sampleNegatives() {
   int ELIM=5000;
   int NLIM=2000;
   if (prod.size()==2) {
      if (indNeg.size() > ELIM) {
	 random_shuffle(indNeg.begin(), indNeg.end());
	 indNeg.resize(ELIM);
      }
   }
   else {
      if (indPos.size() > NLIM) {
	 random_shuffle(indPos.begin(), indPos.end());
	 indPos.resize(NLIM);
      }
      if (indNeg.size() > NLIM) {
	 random_shuffle(indNeg.begin(), indNeg.end());
	 indNeg.resize(NLIM);
      }
      if (indNeg1.size() > NLIM) {
	 random_shuffle(indNeg1.begin(), indNeg1.end());
	 indNeg1.resize(NLIM);
      }
      if (indNeg2.size() > NLIM) {
	 random_shuffle(indNeg2.begin(), indNeg2.end());
	 indNeg2.resize(NLIM);
      }
      for (int i=0; i<indNeg1.size(); ++i) {
	 indNeg.push_back(indNeg1[i]);
      }
      indNeg1.clear();
      for (int i=0; i<indNeg2.size(); ++i) {
	 indNeg.push_back(indNeg2[i]);
      }
      indNeg2.clear();
   }
}

ostream& classifier::write(ostream &out) const {
   /*
   out<<prod[0]<<" -> ";
   for (int i=1; i<prod.size(); ++i) {
      out<<prod[i]<<" ";
   }
   out<<"\n";
   return out;
   if (st) out<<"START type\n";
   if (term) out<<"TERMINAL type\n";
   if (!st && !term) out<<"NON-TERMINAL type\n";
   cout<<"\n\n";   
   */

   /*
   out<<"<pos>\n";
   out<<indPos.size();
   for (int i=0; i<indPos.size(); ++i) {
      out<<"<eg> ";
      sw.writeStr(cout,indPos[i]);
      out<<"</eg>\n";
   }
   out<<"</pos>\n\n";
   out<<"<neg>\n";
   out<<indNeg.size();
   for (int i=0; i<indNeg.size(); ++i) {
      out<<"<eg> ";
      sw.writeStr(cout,indNeg[i]);
      out<<"</eg>\n";
   }
   out<<"</neg>\n\n";
   */
   return out;
}
